#ifndef _UTILS_FUNCTION_H_
#define _UTILS_FUNCTION_H_

namespace utils
{

namespace detail
{

template <typename _Return, typename... _Args>
class abstract_function
{
public:

  virtual _Return operator ()(_Args... args) = 0;
  virtual abstract_function* clone(void) const = 0;
};

template <typename _Object, typename _Return, typename... _Args>
class member_function
    : public abstract_function<_Return, _Args...>
{
public:

  typedef _Return (_Object::*member_fn)(_Args...);

  member_function(_Object* obj, member_fn fn)
    : _object(obj)
    , _function(fn) {}

  _Return operator ()(_Args... args)
  { return _object->*_function(args...); }

  abstract_function<_Return, _Args...>* clone(void) const
  { return new member_function(*this); }

private:

  _Object* _object;
  member_fn _function;
};

template <typename _Return, typename... _Args>
class function
    : public abstract_function<_Return, _Args...>
{
public:

  typedef _Return (*fn)(_Args...);

  function(fn f) : _function(f) {}

  _Return operator ()(_Args... args)
  { return _function(args...); }

  abstract_function<_Return, _Args...>* clone(void) const
  { return new function(*this); }

private:

  fn _function;
};

template <typename _Lambda, typename _Return, typename... _Args>
class lambda
    : public abstract_function<_Return, _Args...>
{
public:

  lambda(_Lambda const& lmbd)
    : _lambda(lmbd) {}

  _Return operator ()(_Args... args)
  { return _lambda(args...); }

  abstract_function<_Return, _Args...>* clone(void) const
  { return new lambda(*this); }

private:

  _Lambda _lambda;
};

} //detail

template <typename _Function>
class function {};

template <typename _Return, typename... _Args>
class function<_Return(_Args...)>
{
public:

  template <typename _Object>
  function(_Object* obj, _Return (_Object::*member_fn)(_Args...))
    : _function(new detail::member_function<_Object, _Return, _Args...>(obj, member_fn)) {}

  function(_Return (*fn)(_Args...))
    : _function(new detail::function<_Return, _Args...>(fn)) {}

  template <typename _Lambda>
  function(_Lambda const& lmbd)
    : _function(new detail::lambda<_Lambda, _Return, _Args...>(lmbd)) {}

  function(function const& fn)
    : _function(fn._function->clone()) {}

  ~function(void) { delete _function; }

  _Return operator ()(_Args... args)
  { return _function->operator ()(args...); }

private:

  detail::abstract_function<_Return, _Args...>* _function;
};

template <typename _Return, typename... _Args>
function<_Return(_Args...)> make_function(
    _Return (*fn)(_Args...))
{ return function<_Return(_Args...)>(fn); }

template <typename _Object, typename _Return, typename... _Args>
function<_Return(_Args...)> make_function(
    _Object* obj, _Return (_Object::*member_fn)(_Args...))
{ return function<_Return(_Args...)>(obj, member_fn); }

} // utils

#endif // _UTILS_FUNCTION_H_
